<?php
	/**
	 * <version greek="beta" major="1" minor="0">Cochin Splash Hen</version>
	 * <author email="njones@art-k-tec.com">Nika Jones</author>
	 * <copyright start="2007">Art-k-tec Systems, LLC</copyright>
	 * <license type="Apache2.0" href="http://www.opensource.org/licenses/apache2.0.php">Apache License, Version 2.0</license>
	 * <summary>
	 *    This is a class that provides a consistant HTTP interface for
	 *    libraries whether Curl or fSockets are installed. It also keeps
	 *    Query strings for GET and POST requests neat and in order.
	 * <summary>
	 */
	 
	class bHttpResponse {
	
		private $sResponse;
		private $aResponseHeaders;
		
		public function __construct($sResponse, $aResponseHeader) {
		
			$this->sResponse =$sResponse;
			$this->aResponseHeaders =$aResponseHeader;
		}
		
		public function Headers() { return $this->aResponseHeaders; }
		public function __toString() { return $this->sResponse; }
	}
	
	class bHttp extends ArrayObject {
		
		const NULL ="\0";
		
		const GET ='GET';
		const PUT ='PUT';
		const POST ='POST';
		const DELETE ='DELETE';
		
		const THROUGH_FILE ='File';
		const THROUGH_CURL ='CURL';
		
		const BASE_URI = 'base';
		
		const DECODE_EXCLAIM	=1;
		const RAW_CONTENT		=2;
		const KEY_VALUE_FORM	=4;
		
		protected $sUrl;
		protected $aUrl;
		protected $sHttpMethod;
		protected $sPhpMethod;
		
		private $aHeader;
		private $sFragment;
		
		public static function version() { return self::$nMajor.'.'.self::$nMinor.'.'.self::$sRevision; }
		
		public function __construct($sUrl =null, $aQueryParameters =null) {
		
			$this->sPhpMethod =self::THROUGH_FILE; // can be THROUGH_CURL
		
			$this->sUrl =$sUrl;
			$this->aUrl =parse_url($sUrl);
			if(!empty($this->aUrl['query'])) {
				
				// double explosion of the querystring
				foreach(explode('&', $this->aUrl['query']) as $sKeyValue) {
					
					list($sKey, $sValue) =explode('=', $sKeyValue);
					$this[$sKey] = urldecode($sValue);
				}
				
				$this->aUrl['query'] =null;
			}
			
			if(is_array($aQueryParameters)) {
			
				foreach($aQueryParameters as $sKey => $sValue) {
					
					$this[$sKey] = $sValue;
				}
			}
		}
		
		public function HttpMethod($sMethod =self::NULL) {
			
			if($sMethod ==self::NULL) { return $this->sHttpMethod; }
			
			switch($sMethod) {
			
				case self::GET: case self::PUT: case self::POST: case self::DELETE:
					$this->sHttpMethod =$sMethod;
				break;
				default:
					trigger_error('Usually only GET, PUT, POST and DELETE are used. You have '.$sMethod, E_NOTICE);
			}
			
			return $this;

		}
		
		public function Header($mKey, $mValue =self::NULL) {
			
			if(is_string($mKey)) {
				
				$mKey =array($mKey=>$mValue);
			}
			
			foreach($mKey as $sKey => $sValue) {
			
				$sKey =strtolower($sKey);
				if(!isset($sValue)) { unset($this->aHeader[$sKey]); return $this; }
				if($sValue ==self::NULL) { return isset($this->aHeader[$sKey]) ? $this->aHeader[$sKey] : null; }
			
				$this->aHeader[$sKey] =$sValue;
			}
			return $this;
		}
		
		public function Fragment($sFragment =self::NULL) {
			
			if($sFragment ==self::NULL) { return $this->aUrl['fargment']; }
			
			$this->aUrl['fragment'] =$sFragment;
			$this->sUrl =$this->UnParseUrl($this->aUrl);
			return $this;
		}
		
		public function Path($sPath =self::NULL) {
			
			if($sPath ==self::NULL) { return $this->aUrl['path']; }
			
			$this->aUrl['path'] =$sPath;
			$this->sUrl =$this->UnParseUrl($this->aUrl);
			return $this;
		}
		
		public function Username($sUsername =self::NULL) {
			
			if($sUsername ==self::NULL) { return $this->aUrl['user']; }
			
			$this->aUrl['user'] =$sUsername;
			$this->sUrl =$this->UnParseUrl($this->aUrl);
			return $this;
		}
		
		public function Password($sPassword =self::NULL) {
			
			if($sPassword ==self::NULL) { return $this->aUrl['pass']; }
			
			$this->aUrl['pass'] =$sPassword;
			$this->sUrl =$this->UnParseUrl($this->aUrl);
			return $this;
		}
		
		public function Port($nPort =self::NULL) {
			
			if($nPort ==self::NULL) { return $this->aUrl['port']; }
			
			$this->aUrl['port'] =$nPort;
			$this->sUrl =$this->UnParseUrl($this->aUrl);
			return $this;
		}
		
		public function Hostname($sHostname =self::NULL) {
			
			if($sHostname ==self::NULL) { return $this->aUrl['host']; }
			
			$this->aUrl['host'] =$sHostname;
			$this->sUrl =$this->UnParseUrl($this->aUrl);
			return $this;
		}
		
		public function Scheme($sScheme =self::NULL) {
			
			if($sScheme ==self::NULL) { return $this->aUrl['scheme']; }
			
			$this->aUrl['scheme'] =$sScheme;
			$this->sUrl =$this->UnParseUrl($this->aUrl);
			return $this;
		}
	
		public function QueryString() {
			
			return '?'.http_build_query((array)$this);
		}
		
		public function HttpGet($wOptions =null) {
			
			$this->sHttpMethod =bHttp::GET;
			return $this->Send($wOptions);
		}
		
		public function HttpPost($wOptions =null) {
			
			$this->sHttpMethod =bHttp::POST;
			if(!isset($this->aHeader['content-type'])) {
			
				$this->Header('content-type', 'application/x-www-urlencoded');
			}
			return $this->Send($wOptions);
		}
		
		public function Send($wOptions =null) {
			
			return call_user_func_array(array($this, 'SendBy'.$this->sPhpMethod), $wOptions);
		}
		
		public function SendByFile($wOptions =null) {
			
			$aHead = array('http' => array('method' => $this->sHttpMethod));
			if ($this->aHeader !==null) {
				
				$sHeader ='';
				foreach($this->aHeader as $sKey =>$sValue) { $sHeader .="$sKey: $sValue\r\n"; }
				$aHead['http']['header'] =$sHeader;
			}
			
			$aUrl =$this->aUrl;
			if($wOptions & self::KEY_VALUE_FORM) {
			
				$sContent =implode(array_map(create_function('$a, $b', 'return "$a:$b\n";'), array_keys((array)$this), array_values((array)$this)));
			} else {
			
				$sContent =http_build_query((array)$this);
			}
			
			if($wOptions & self::DECODE_EXCLAIM) {
				
				$sContent =str_replace('%21', '!', $sContent);
			}
			
			if($wOptions & self::RAW_CONTENT) {
				
				$sContent =implode('', (array)$this);
			}
			
			if($this->sHttpMethod ==bHttp::POST) {
				
				if($sContent !='') { $aHead['http']['content'] =$sContent; }
			}
			
			$sUrl =$this->UnParseUrl($aUrl);
			
			if($wOptions & self::DECODE_EXCLAIM) {
				
				$sUrl =str_replace('%21', '!', $sUrl);
			}
			
			$oContext =stream_context_create($aHead);
			$sResponse =@file_get_contents($sUrl, FILE_BINARY, $oContext);
			
			return new bHttpResponse($sResponse, $http_response_header);
		}
		
		public function SendByCurl($wOptions =null) {
			
			$aHeader =array();
			if ($this->aHeader !==null) {
				
				foreach($this->aHeader as $sKey =>$sValue) { $aHeader[] ="$sKey: $sValue\r\n"; }
			}
			
			$rCurl =curl_init();
			curl_setopt($rCurl,CURLOPT_URL, $sUrl);
			curl_setopt($rCurl, CURLOPT_HTTPHEADERS, $aHeader);
			if($aHead['http']['method'] =='POST') {
				
				curl_setopt($rCurl, CURLOPT_POST, 1);
				curl_setopt($rCurl, CURLOPT_POSTFIELDS, http_build_query((array)$this));
			}
			if($aHead['http']['method'] =='PUT') {
				
				curl_setopt($rCurl, CURLOPT_PUT, 4);
			}
			if($aHead['http']['method'] =='DELETE') {
				
				curl_setopt($rCurl, CURLOPT_CUSTOMREQUEST, 'DELETE');
			}
			
			$sReponse = curl_exec($rCurl);
			$aResponseHeader =curl_getinfo($rCurl);
			
			curl_close($rCurl);
	
			return new bHttpResponse($sRepsonse, $aResponseHeader);
		}
		
		public static function NormalizeUrl($sUrl) {
			
			$aUrl =parse_url($sUrl);
			return strtolower("{$aUrl['scheme']}://{$aUrl['host']}").$aUrl['path'];
		}
		
		public static function NormalizeQueryString($aQueryString) {
			
			$sNormalized =http_build_query($aQueryString); // May URL_ENCODE too soon.
			$aNormalized =explode('&', $sNormalized);
			natsort($aNormalized);
			return $aNormalized;
		}
		
		private function UnParseUrl($aUrl) {
			
			$aQuery =(array)$this;
			$sUrl =$aUrl['scheme'].'://';
			$sUrl .=isset($aUrl['user']) ? $aUrl['user'] : '';
			$sUrl .=isset($aUrl['pass']) ? (isset($aUrl['user'])?':':'').$aUrl['pass'] : '';
			$sUrl .=isset($aUrl['user']) ? '@' : '';
			$sUrl .=isset($aUrl['host']) ? $aUrl['host'] : '';
			$sUrl .=isset($aUrl['port']) ? ':'.$aUrl['port'] : '';
			$sUrl .=isset($aUrl['path']) ? $aUrl['path'] : '';
			$sUrl .=!empty($aQuery) && ($this->sHttpMethod ==bHttp::GET || $this->sHttpMethod ==null) ? '?'.http_build_query((array)$this) : '';
			$sUrl .=isset($aUrl['fragment']) ? '#'.$aUrl['fragment'] : '';

			return $sUrl;
		}
				
		public function AsPath() {
			
			return '/'.implode('/', (array)$this); // doesn't work with nested arrays.
		}
		
		public function AsXml() {
			
			if(class_exists(bArray)) {
				
				$oArray =new bArray((array)$this);
				return $oArray->AsXml();
			}
			
			$aXml =(array)$this;
			array_walk_recursive($aXml, create_function('&$v, $k', '$v ="<{$k}>{$v}<{$k}>"'));
			$sRoot =strtolower(get_class($this));
			return "<{$sRoot}>".implode('', $aXml)."</{$sRoot}>";
		}
		
		public function __toString($sMode =null) {
			
			switch($sMode) {
			
				case self::BASE_URI:
					$sReturn =$this->aUrl['scheme'].'://'.$this->aUrl['host'];
				break;
				default:
					$sReturn =$this->UnParseUrl($this->aUrl);
			}
		
			return $sReturn;
		}
	}

?>